#include "../stdafx.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>

#include "../include/Platform.h"

#include "../util/util.h"
#include "Partitioning.h"
#include "RunStyles.h"

namespace soy{

	// Find the first run at a position
	int RunStyles::RunFromPosition(int position) const {
		int run = starts->IndexOf(position);
		// Go to first element with this position
		while ((run > 0) && (position == starts->GetValue(run-1))) {
			run--;
		}
		return run;
	}

	// If there is no run boundary at position, insert one continuing style.
	int RunStyles::SplitRun(int position) {
		int run = RunFromPosition(position);
		int posRun = starts->GetValue(run);
		if (posRun < position) {
			int runStyle = ValueAt(position);
			run++;
			starts->InsertValue(run, position);
			styles->InsertValue(run, 1, runStyle);
		}
		return run;
	}

	void RunStyles::RemoveRun(int run) {
		starts->RemoveValue(run);
		styles->DeleteRange(run, 1);
	}

	void RunStyles::RemoveRunIfEmpty(int run) {
		if ((run < starts->Partitions()) && (starts->Partitions() > 1)) {
			if (starts->GetValue(run) == starts->GetValue(run+1)) {
				RemoveRun(run);
			}
		}
	}

	void RunStyles::RemoveRunIfSameAsPrevious(int run) {
		if ((run > 0) && (run < starts->Partitions())) {
			if (styles->ValueAt(run-1) == styles->ValueAt(run)) {
				RemoveRun(run);
			}
		}
	}

	RunStyles::RunStyles() {
		starts = new Partitioning(8);
		styles = new SplitVector<int>();
		styles->InsertValue(0, 2, 0);
	}

	RunStyles::~RunStyles() {
		delete starts;
		starts = NULL;
		delete styles;
		styles = NULL;
	}

	int RunStyles::Length() const {
		return starts->GetValue(starts->Partitions());
	}

	int RunStyles::ValueAt(int position) const {
		return styles->ValueAt(starts->IndexOf(position));
	}

	int RunStyles::FindNextChange(int position, int end) {
		int run = starts->IndexOf(position);
		if (run < starts->Partitions()) {
			int runChange = starts->GetValue(run);
			if (runChange > position)
				return runChange;
			int nextChange = starts->GetValue(run + 1);
			if (nextChange > position) {
				return nextChange;
			} else if (position < end) {
				return end;
			} else {
				return end + 1;
			}
		} else {
			return end + 1;
		}
	}

	int RunStyles::StartRun(int position) {
		return starts->GetValue(starts->IndexOf(position));
	}

	int RunStyles::EndRun(int position) {
		return starts->GetValue(starts->IndexOf(position) + 1);
	}

	bool RunStyles::FillRange(int &position, int value, int &fillLength) {
		int end = position + fillLength;
		int runEnd = RunFromPosition(end);
		if (styles->ValueAt(runEnd) == value) {
			// End already has value so trim range.
			end = starts->GetValue(runEnd);
			if (position >= end) {
				// Whole range is already same as value so no action
				return false;
			}
			fillLength = end - position;
		} else {
			runEnd = SplitRun(end);
		}
		int runStart = RunFromPosition(position);
		if (styles->ValueAt(runStart) == value) {
			// Start is in expected value so trim range.
			runStart++;
			position = starts->GetValue(runStart);
			fillLength = end - position;
		} else {
			if (starts->GetValue(runStart) < position) {
				runStart = SplitRun(position);
				runEnd++;
			}
		}
		if (runStart < runEnd) {
			styles->SetValueAt(runStart, value);
			// Remove each old run over the range
			for (int run=runStart+1; run<runEnd; run++) {
				RemoveRun(runStart+1);
			}
			runEnd = RunFromPosition(end);
			RemoveRunIfSameAsPrevious(runEnd);
			RemoveRunIfSameAsPrevious(runStart);
			runEnd = RunFromPosition(end);
			RemoveRunIfEmpty(runEnd);
			return true;
		} else {
			return false;
		}
	}

	void RunStyles::SetValueAt(int position, int value) {
		int len = 1;
		FillRange(position, value, len);
	}

	void RunStyles::InsertSpace(int position, int insertLength) {
		int runStart = RunFromPosition(position);
		if (starts->GetValue(runStart) == position) {
			int runStyle = ValueAt(position);
			// Inserting at start of run so make previous longer
			if (runStart == 0) {
				// Inserting at start of document so ensure 0
				if (runStyle) {
					styles->SetValueAt(0, 0);
					starts->InsertValue(1, 0);
					styles->InsertValue(1, 1, runStyle);
					starts->OnInsertText(0, insertLength);
				} else {
					starts->OnInsertText(runStart, insertLength);
				}
			} else {
				if (runStyle) {
					starts->OnInsertText(runStart-1, insertLength);
				} else {
					// Insert at end of run so do not extend style
					starts->OnInsertText(runStart, insertLength);
				}
			}
		} else {
			starts->OnInsertText(runStart, insertLength);
		}
	}

	void RunStyles::DeleteAll() {
		delete starts;
		starts = NULL;
		delete styles;
		styles = NULL;
		starts = new Partitioning(8);
		styles = new SplitVector<int>();
		styles->InsertValue(0, 2, 0);
	}

	void RunStyles::DeleteRange(int position, int deleteLength) {
		int end = position + deleteLength;
		int runStart = RunFromPosition(position);
		int runEnd = RunFromPosition(end);
		if (runStart == runEnd) {
			// Deleting from inside one run
			starts->OnInsertText(runStart, -deleteLength);
		} else {
			runStart = SplitRun(position);
			runEnd = SplitRun(end);
			starts->OnInsertText(runStart, -deleteLength);
			// Remove each old run over the range
			for (int run=runStart; run<runEnd; run++) {
				RemoveRun(runStart);
			}
			RemoveRunIfEmpty(runStart);
			RemoveRunIfSameAsPrevious(runStart);
		}
	}

	int RunStyles::Runs() const {
		return starts->Partitions();
	}

	bool RunStyles::AllSame() const {
		for (int run = 1; run < starts->Partitions(); run++) {
			if (styles->ValueAt(run) != styles->ValueAt(run - 1))
				return false;
		}
		return true;
	}

	bool RunStyles::AllSameAs(int value) const {
		return AllSame() && (styles->ValueAt(0) == value);
	}

	int RunStyles::Find(int value, int start) const {
		if (start < Length()) {
			int run = start ? RunFromPosition(start) : 0;
			if (styles->ValueAt(run) == value)
				return start;
			run++;
			while (run < starts->Partitions()) {
				if (styles->ValueAt(run) == value)
					return starts->GetValue(run);
				run++;
			}
		}
		return -1;
	}

};
